use clap::Parser;
use std::path::PathBuf;
use regex::Regex;
use std::num::NonZeroUsize;
use num_cpus;

/// 文件扫描工具，计算文件的MD5值并存储到数据库中
#[derive(Parser)]
#[command(name = "filescanner")]
#[command(version, about = "扫描文件并计算MD5值", long_about = None)]
pub struct Cli {
    /// 要扫描的目录路径
    #[arg(short = 'd', long = "directory", value_name = "DIR")]
    pub directory: Option<PathBuf>,

    /// 忽略小于指定大小的文件 (例如: 100k, 1M)
    /// 默认值为 100k
    #[arg(short = 's', long = "size", value_name = "SIZE", default_value = "100k")]
    pub min_size: String,

    /// 指定工作线程数
    /// 默认为CPU核心数的1.5倍
    #[arg(short = 'w', long = "workers", value_name = "NUM")]
    pub workers: Option<NonZeroUsize>,

    /// 检查数据库中的文件是否存在
    /// 如果文件不存在，则删除该记录
    #[arg(short = 'c', long = "check")]
    pub check: bool,

    /// 查询MD5相同的文件，按文件从大到小排序
    #[arg(short = 'q', long = "query")]
    pub query: bool,
}

/// 将表示文件大小的字符串转换为字节数
/// 例如: "100k" -> 102400, "1M" -> 1048576
pub fn parse_size(size_str: &str) -> Result<u64, String> {
    // 正则表达式匹配数字和可选的单位后缀
    let re = Regex::new(r"^(\d+)([kKmMgG])?$").unwrap();

    match re.captures(size_str) {
        Some(caps) => {
            // 解析数字部分
            let value: u64 = caps.get(1).unwrap().as_str().parse()
                .map_err(|_| format!("无法解析大小值: {}", size_str))?;

            // 解析单位部分，计算实际字节数
            match caps.get(2).map(|m| m.as_str().to_lowercase()) {
                Some(suffix) if suffix == "k" => Ok(value * 1024),
                Some(suffix) if suffix == "m" => Ok(value * 1024 * 1024),
                Some(suffix) if suffix == "g" => Ok(value * 1024 * 1024 * 1024),
                None => Ok(value),  // 没有单位，直接返回原值
                Some(suffix) => Err(format!("不支持的大小单位: {}", suffix)),
            }
        },
        None => Err(format!("无效的大小格式: {}", size_str)),
    }
}

/// 根据CPU数量计算默认线程数
/// 默认为CPU核心数的1.5倍
pub fn default_worker_count() -> NonZeroUsize {
    let cpu_count = num_cpus::get();
    let default_count = (cpu_count as f32 * 1.5).round() as usize;
    NonZeroUsize::new(std::cmp::max(1, default_count)).unwrap()
}
